﻿using System;
using System.Collections.Generic;
using System.Collections;
using linq = Microsoft.Linq.Expressions;
using Bling.Util;


namespace Bling.Core {
  using Bling.DSL;
  using Bling.Ops;

  public partial interface IBrandT  {
    /// <summary>
    /// Coerce this brand to another.
    /// </summary>
    /// <typeparam name="BRAND0">Type to coerce to.</typeparam>
    /// <returns>Coerced brand.</returns>
    BRAND0 Coerce<BRAND0>() where BRAND0 : Brand<BRAND0>;
    /// <summary>
    /// Reveal the self and underlying type of this brand via a visitor.
    /// </summary>
    /// <typeparam name="R">Type to be returned by visitor.</typeparam>
    /// <param name="Reveal">Reveal type visitor.</param>
    /// <returns>Value returned by visitor</returns>
    R Reveal<R>(BrandTypeReveal<R> Reveal);
    /// <summary>
    /// Get the underlying expression of this brand.
    /// </summary>
    Expr Provider { get; }
  }
  public partial interface ICoreValue {
    StringBl ToStringBl();
  }
  public partial interface ICoreValue<BRAND> : ICoreValue, IConditionTarget<BRAND>, ICanTable<BRAND> where BRAND : ICoreValue<BRAND> {
  }

  /// <summary>
  /// Interface type for brand with self type but without underlying type.
  /// </summary>
  /// <typeparam name="BRAND">Self type of brand.</typeparam>
  public partial interface ICanBind<BRAND> : ICoreValue<BRAND> where BRAND : ICanBind<BRAND> {
    /// <summary>
    /// Convert to brand type.
    /// </summary>
    BRAND AsBrand { get; }
    /// <summary>
    /// Bind this brand to another, brand is defined according to the underlying expression.
    /// </summary>
    BRAND Bind { get; set; }

    BRAND TwoWay { get; set; }

    /// <summary>
    /// Like bind, but with immediate semantics, defined according to the underlying expression.
    /// </summary>
    BRAND Now { get; set; }
  }

  public static class CanWrap<BRAND, T> where BRAND : ICanBind<BRAND> {
    public static Func<Expr<T>, BRAND> To { get; set; }
    public static Func<BRAND, Expr<T>> From { get; set; }
  }

  /*
  public partial interface ICanWrap<BRAND, T> : IBrand<BRAND> where BRAND : IBrand<BRAND> { }
  */

  /// <summary>
  /// Interface type for brand with underlying type but without self type.
  /// </summary>
  /// <typeparam name="T">Underlying type.</typeparam>
  public partial interface IBrandT<T> : IBrandT {
    /// <summary>
    /// Get the underlying expression of this brand.
    /// </summary>
    new Expr<T> Provider { get; }
  }
  public partial interface IBrand<T, BRAND> : ICanBind<BRAND>, IBrandT<T> where BRAND : IBrand<T, BRAND> { }

  /// <summary>
  /// Visitor that reveals the type of a brand.
  /// </summary>
  /// <typeparam name="R">Type of value returned by visitor.</typeparam>
  public interface BrandTypeReveal<R> {
    /// <summary>
    /// Visit method that reveals underlying and self types of brand.
    /// </summary>
    /// <typeparam name="T">Underlying type</typeparam>
    /// <typeparam name="BRAND">Self type</typeparam>
    /// <param name="value">Brand being visited</param>
    /// <returns>Result of visit</returns>
    R Visit<T, BRAND>(Brand<T, BRAND> value) where BRAND : Brand<T, BRAND>;
  }

  /// <summary>
  /// Specified that this brand extends the brand TO.
  /// </summary>
  /// <typeparam name="TO">A brand type extended by implementor of this interface</typeparam>
  public partial interface IExtends<TO>  where TO : Brand<TO> {
    //TOX Coerce0<TOX>() where TOX : Brand<TOX>;
  }
  public partial interface IExtends<BRAND, TO> : IExtends<TO>, ICanBind<BRAND>
    where BRAND : Brand<BRAND>
    where TO : Brand<TO> { }

  public partial interface BrandTypeRevealS<R> {
    R Visit<T0, BRAND0>() where BRAND0 : Brand<T0,BRAND0>;
  }

  /// <summary>
  /// Base brand class without specified self or underlying types.
  /// </summary>
  public abstract partial class Brand {
    internal interface BrandTypeRevealT {
      object Visit<T0, BRAND0>() where BRAND0 : Brand<T0, BRAND0>;
    }

    public static Brand CreateInstance(Type BrandType, Expr e) {
      var cnt = BrandType.GetConstructor(new Type[] { e.GetType() });
      var brand = (Brand)cnt.Invoke(new object[] { e });
      return brand;
    }
    public abstract Brand CopyMetaTo(Brand Other);

    public Brand Reorient(Expr e) {
      var ret = CreateInstance(this.GetType(), e);
      return CopyMetaTo(ret);
    }

    private static readonly Dictionary<Type, Func<Expr, Brand>> ToBrands = new Dictionary<Type, Func<Expr, Brand>>();

    protected static void RegisterT<T,BRAND>(Func<Expr<T>,BRAND> ToBrand) where BRAND : Brand<T,BRAND> {
      (ToBrand != null).Assert();
      (!ToBrands.ContainsKey(typeof(T))).Assert();
      ToBrands[typeof(T)] = e => ToBrand((Expr<T>)e);
    }

    /// <summary>
    /// Create a brand (type unspecified) for an underlying expression.
    /// </summary>
    /// <param name="e">Provider expression</param>
    /// <returns>A brand for the underlying expression</returns>
    public static Brand ToBrand(Expr e) { return ToBrands[e.TypeOfT](e); }
    public static Func<Expr,Brand> ToBrand(Type BrandType) {
      //if (ToBrands.ContainsKey(e.TypeOfT)) return ToBrands[e.TypeOfT](e);
      var TypeOfT = Brand.TypeOfT(BrandType);
      var constructor = BrandType.GetConstructor(new Type[] { typeof(Expr<>).MakeGenericType(TypeOfT) });
      (constructor != null).Assert();
      return e => (Brand) constructor.Invoke(new object[] { e });
    }
    /// <summary>
    /// Get the underlying expression of this brand.
    /// </summary>
    protected abstract Expr BaseProvider { get; }

    public Expr Provider { get { return BaseProvider; } }


    /// <summary>
    /// Coerce this brand to another.
    /// </summary>
    /// <typeparam name="BRAND0">Type to coerce to.</typeparam>
    /// <returns>Coerced brand.</returns>
    public abstract BRAND0 Coerce<BRAND0>() where BRAND0 : Brand<BRAND0>;
    /// <summary>
    /// Reveal the self and underlying type of this brand via a visitor.
    /// </summary>
    /// <typeparam name="R">Type to be returned by visitor.</typeparam>
    /// <param name="Reveal">Reveal type visitor.</param>
    /// <returns>Value returned by visitor</returns>
    public abstract R Reveal<R>(BrandTypeReveal<R> Reveal);

    public static Type TypeOfT(Type Type) {
      var Type0 = (Type)typeof(Brand<>).MakeGenericType(Type);
      Type0 = (Type)Type0.GetProperty("TypeOfT").GetAccessors()[0].Invoke(null, new object[0]);
      return Type0;
    }
  }

  public partial interface ICanTable<SELF> {
  }
  public partial interface IConditionTarget<BRAND> {
    BRAND ChooseA(BoolBl Test, BRAND Other);
    
  }
  public partial interface ICanMakeParam<BRAND> : ICoreValue<BRAND> where BRAND : ICanMakeParam<BRAND> { }
  public static class Params<BRAND> where BRAND : ICanMakeParam<BRAND> {
    public static Func<object, string, BRAND> Make0;
    public static Func<object, BRAND> Make {
      get {
        return obj => Make0(obj, obj.ToString());
      }
    }
  }


  /// <summary>
  /// Base brand class with specified self type but without underlying type.
  /// </summary>
  public abstract partial class Brand<BRAND> : Brand, ICanBind<BRAND>, ICanMakeParam<BRAND> where BRAND : Brand<BRAND> {
    private static Type TypeOfT0;
    public abstract StringBl ToStringBl();

    public Brand() {}

    public new BRAND Reorient(Expr e) {
      return CopyMetaTo(ToBrand(e));
    }


    public new static Type TypeOfT {
      get {
        if (TypeOfT0 == null) {
          // blah, use reflection to initialize it!
          var tpe = typeof(BRAND);
          while (!tpe.IsGenericType || tpe.GetGenericTypeDefinition() != typeof(Brand<,>))
            tpe = tpe.BaseType;
          var TypeOfT = tpe.GetGenericArguments()[0];
          var value = typeof(Constant<>).MakeGenericType(TypeOfT).GetConstructor(new Type[0]).Invoke(new object[0]);

          value = typeof(BRAND).GetConstructor(new Type[] {
          typeof(Expr<>).MakeGenericType(TypeOfT),
          }).Invoke(new object[] { value });
          (TypeOfT0 == TypeOfT).Assert();
        }
        return TypeOfT0;
      }
      internal set {
        TypeOfT0 = value;
      }
    }
   

    protected static Func<Expr, BRAND> BaseToBrand { set; private get; }

    public new static BRAND ToBrand(Expr e) { return BaseToBrand(e); }


    private class BrandTypeRevealU<R> : BrandTypeRevealT {
      internal BrandTypeRevealS<R> V;
      public object Visit<T0, BRAND0>() where BRAND0 : Brand<T0, BRAND0> { return V.Visit<T0, BRAND0>(); }
    }

    public static R RevealType<R>(BrandTypeRevealS<R> visitor) {
      if (RevealTypeF == null) {
        typeof(BRAND).GetConstructor(new Type[0]).Invoke(new object[0]);
      }
      return (R)RevealTypeF(new BrandTypeRevealU<R>() { V = visitor }); 
    }
    static internal Func<BrandTypeRevealT, object> RevealTypeF { get; set; }

    public static Func<Brand, BRAND> UpCast0 { get; protected set; }


    /// <summary>
    /// Convert to brand type.
    /// </summary>
    public BRAND AsBrand { get { return (BRAND)((object)this); } }
    public static implicit operator BRAND(Brand<BRAND> brand) { return brand.AsBrand; }
    /// <summary>
    /// Bind this brand to another, brand is defined according to the underlying expression.
    /// </summary>
    public abstract BRAND Bind { get; set; }
    public abstract BRAND TwoWay { get; set; }

    /// <summary>
    /// Like bind, but with immediate semantics, defined according to the underlying expression.
    /// </summary>
    public abstract BRAND Now { get; set; }
    internal interface WithTServices {
      BRAND CoerceFrom<T0, BRAND0>(BRAND0 value) where BRAND0 : Brand<T0, BRAND0>;
      BRAND DownCastFrom<T0, BRAND0>(BRAND0 value) where BRAND0 : Brand<T0, BRAND0>, IExtends<BRAND0,BRAND>;
    }

    internal static WithTServices TServices { private get; set; }
    internal static BRAND CoerceFrom<T0, BRAND0>(BRAND0 value) where BRAND0 : Brand<T0, BRAND0> {
      return TServices.CoerceFrom<T0, BRAND0>(value);
    }
    internal static BRAND DownCastFrom<T0, BRAND0>(BRAND0 value) where BRAND0 : Brand<T0, BRAND0>, IExtends<BRAND0, BRAND> {
      return TServices.DownCastFrom<T0, BRAND0>(value);
    }


    internal static BRAND Default0;
    /// <summary>
    /// Brand with underlying default value expression.
    /// </summary>
    public static BRAND Default {
      get {
        (Default0 != null).Assert();
        return Default0;
      }
      protected set { Default0 = value; } }

    public virtual BRAND CopyMetaTo(BRAND Other) { return Other;  }
    public override Brand CopyMetaTo(Brand Other) {
      return CopyMetaTo((BRAND)Other);
    }
    public static Func<BRAND, BRAND, Action> Assign { get; protected set; }

    static Brand() {
      if (!typeof(BRAND).IsAbstract) {
        var cnt = typeof(BRAND).GetConstructor(new Type[] { });
        (cnt != null).Assert();
        try {
          cnt.Invoke(new object[] { });
        } catch (Exception e) {
          false.Assert();
        }
      }
    }
  }
  public partial interface ICoreBrandT { }


  public struct BrandId<T, BRAND> where BRAND : Brand<T, BRAND> { }
  /*
  public static class Converter<FROM, TO>
    where FROM : Brand<FROM>
    where TO : Brand<TO> {
    public static Func<FROM, TO> Apply;
  }*/


  /// <summary>
  /// Brand class that specified both self and underlying types.
  /// </summary>
  /// <typeparam name="T">Type of underlying expression</typeparam>
  /// <typeparam name="BRAND">Self type</typeparam>
  public abstract partial class Brand<T, BRAND> : Brand<BRAND>, IBrandT<T>, IExtends<BRAND, ObjectBl>, IBrand<T,BRAND> where BRAND : Brand<T, BRAND> {


    public static BrandId<T, BRAND> Id { get { return new BrandId<T, BRAND>(); } }

    public BRAND Reorient(Expr<T> e) {
      return CopyMetaTo(ToBrand(e));
    }
    /*
    public virtual TOX Coerce0<TOX>() where TOX : Brand<TOX> {
      if (!(this is IExtends<BRAND, TOX>)) throw new NotSupportedException();
      return Converter<BRAND, TOX>.Apply(this);
    }
    */

    /// <summary>
    /// Factory that converts underlying expression into brand.
    /// </summary>
    public new static readonly Func<Expr<T>, BRAND> ToBrand;
    /// <summary>
    /// The expression that provides a value for this brand.
    /// </summary>
    public new virtual Expr<T> Provider { get { return Provider0; } }

    private readonly Expr<T> Provider0;


    public bool IsConstant { get { return Provider.IsConstant; } }

    /// <summary>
    /// Provider expression
    /// </summary>
    protected override Expr BaseProvider { get { return Provider; } }
    public Brand(Expr<T> Provider) { this.Provider0 = Provider; }
    public Brand() : base() {} // this(((object)Default0) != null ? Default0.Underlying : new Constant<T>(default(T))) { }
    public override string ToString() { return Provider.ToString(); }
    private static void RegisterT() {
      RegisterT<T, BRAND>(ToBrand);
    }
    public override R Reveal<R>(BrandTypeReveal<R> Reveal) {
      return Reveal.Visit(this);
    }
    /// <summary>
    /// Create a string representation of this brand in expression form (result is also a brand).
    /// </summary>
    public override StringBl ToStringBl() {
      return ObjectBl.UpCast(this).ToStringBl();
    }
    public static implicit operator StringBl(Brand<T,BRAND> Value) {
      return Value.ToStringBl();
    }
    public static implicit operator ObjectBl(Brand<T, BRAND> Value) {
      return ObjectBl.UpCast(Value);
    }
    internal protected virtual BRAND AsRHS(BRAND Other) { return Other; }
    /// <summary>
    /// Bind this brand to another, brand is defined according to the providing expression.
    /// </summary>
    public override BRAND Bind {
      get { return this; }
      set { Provider.Bind = AsRHS(value).Provider; }
    }
    /// <summary>
    /// Bind this brand to another.
    /// </summary>
    public override BRAND TwoWay {
      get { return this; }
      set { Provider.TwoWay = AsRHS(value).Provider; }
    }

    /// <summary>
    /// Like bind, but with immediate semantics, defined according to the underlying expression.
    /// </summary>
    public override BRAND Now {
      get { return this.CopyMetaTo(ToBrand(new NowExpr<T>(Provider))); }
      set {
        var assign = new NowAssign<Action>();
        if (Provider.Solve(assign, AsRHS(value))) {
          foreach (var r in assign.Results) r();
        } else throw new NotSupportedException();
      }
    }
    /// <summary>
    /// Creates a function that executes the assignment LHS = RHS.
    /// </summary>
    /// <param name="LHS">The Bl value being assigned to.</param>
    /// <param name="RHS">The Bl value being assigned from.</param>
    /// <returns>Function that executes the assignment.</returns>
    public new static Action Assign(BRAND LHS, BRAND RHS) {
      var assign = new NowAssign<Action>();
      if (!LHS.Provider.Solve(assign, LHS.AsRHS(RHS))) throw new NotSupportedException();
      var list = assign.Results;
      if (list.Count == 1) {
        return list[0];
      } else return () => {
        foreach (var n in list) n();
      };
    }
    /*
    public static Action Assign(BRAND LHS, Func<BRAND> RHS) {
      return Assign(LHS, LHS.AsRHS(RHS()));
    }*/
    /// <summary>
    /// Creates a one argument function that executes the assignment s => LHS(s) = RHS(s), 
    /// where s is bound to the specified argument.
    /// </summary>
    /// <typeparam name="S">Type of the argument.</typeparam>
    /// <param name="LHS">Expression being assigned to.</param>
    /// <param name="RHS">Expression being assigned from.</param>
    /// <returns>Function that executes the assignment.</returns>
    public static Action<S> Assign<S>(Func<Expr<S>, BRAND> LHS, Func<Expr<S>, BRAND, BRAND> RHS) {
      var ArgS = new ParameterExpr<S>("ArgS");
      var assign = new NowAssign<Action<S>>(ArgS);
      var LHS0 = LHS(ArgS);
      var RHS0 = LHS0.AsRHS(RHS(ArgS, LHS0));
      if (!LHS0.Provider.Solve(assign, RHS0)) throw new NotSupportedException();
      var list = assign.Results;
      // flatten the list!
      if (list.Count == 0) throw new NotSupportedException();
      else if (list.Count == 1) return list[0];
      else return arg => {
        foreach (var n in list) n(arg);
      };
    }
    public Action<T> MakeAssign {
      get {
        return Assign<T>((t) => this, (t, lhs) => ToBrand(t));

      }
    }

    /// <summary>
    /// Creates a two argument function that executes the assignment s,u => LHS(s,u) = RHS(s,u), 
    /// where s,u are bound to specified arguments.
    /// </summary>
    /// <typeparam name="S">Type of the first argument.</typeparam>
    /// <typeparam name="U">Type of the second argument.</typeparam>
    /// <param name="LHS">Expression being assigned to.</param>
    /// <param name="RHS">Expression being assigned from.</param>
    /// <returns>Function that executes the assignment.</returns>
    public static Action<S, U> Assign<S, U>(Func<Expr<S>, Expr<U>, BRAND> LHS, Func<Expr<S>, Expr<U>, BRAND, BRAND> RHS) {
      var ArgS = new ParameterExpr<S>("ArgS");
      var ArgU = new ParameterExpr<U>("ArgU");
      var assign = new NowAssign<Action<S, U>>(ArgS, ArgU);
      var LHS0 = LHS(ArgS, ArgU);
      var RHS0 = LHS0.AsRHS(RHS(ArgS, ArgU, LHS0));
      if (!LHS0.Provider.Solve(assign, RHS0)) throw new NotSupportedException();
      var list = assign.Results;
      // flatten the list!
      if (list.Count == 0) throw new NotSupportedException();
      else if (list.Count == 1) return list[0];
      else return (argA, argB) => {
        foreach (var n in list) n(argA, argB);
      };
    }
    /// <summary>
    /// Creates a three argument function that executes the assignment s,u,v => LHS(s,u,v) = RHS(s,u,v), 
    /// where s,u,v are bound to specified arguments.
    /// </summary>
    /// <typeparam name="S">Type of the first argument.</typeparam>
    /// <typeparam name="U">Type of the second argument.</typeparam>
    /// <typeparam name="V">Type of the third argument.</typeparam>
    /// <param name="LHS">Expression being assigned to.</param>
    /// <param name="RHS">Expression being assigned from.</param>
    /// <returns>Function that executes the assignment.</returns>
    public static Action<S, U, V> Assign<S, U, V>(Func<Expr<S>, Expr<U>, Expr<V>, BRAND> LHS, Func<Expr<S>, Expr<U>, Expr<V>, BRAND, BRAND> RHS) {
      var ArgS = new ParameterExpr<S>("ArgS");
      var ArgU = new ParameterExpr<U>("ArgU");
      var ArgV = new ParameterExpr<V>("ArgV");
      var assign = new NowAssign<Action<S, U, V>>(ArgS, ArgU, ArgV);
      var LHS0 = LHS(ArgS, ArgU, ArgV);
      var RHS0 = LHS0.AsRHS(RHS(ArgS, ArgU, ArgV, LHS0));
      if (!LHS0.Provider.Solve(assign, RHS0)) throw new NotSupportedException();
      var list = assign.Results;
      // flatten the list!
      if (list.Count == 0) throw new NotSupportedException();
      else if (list.Count == 1) return list[0];
      else return (argA, argB, argC) => {
        foreach (var n in list) n(argA, argB, argC);
      };
    }
    /// <summary>
    /// Creates a four argument function that executes the assignment s,u,v,w => LHS(s,u,v,w) = RHS(s,u,v,w), 
    /// where s,u,v are bound to specified arguments.
    /// </summary>
    /// <typeparam name="S">Type of the first argument.</typeparam>
    /// <typeparam name="U">Type of the second argument.</typeparam>
    /// <typeparam name="V">Type of the third argument.</typeparam>
    /// <typeparam name="W">Type of the fourth argument.</typeparam>
    /// <param name="LHS">Expression being assigned to.</param>
    /// <param name="RHS">Expression being assigned from.</param>
    /// <returns>Function that executes the assignment.</returns>
    public static Action<S, U, V, W> Assign<S, U, V, W>(Func<Expr<S>, Expr<U>, Expr<V>, Expr<W>, BRAND> LHS, Func<Expr<S>, Expr<U>, Expr<V>, Expr<W>, BRAND> RHS) {
      var ArgS = new ParameterExpr<S>("ArgS");
      var ArgU = new ParameterExpr<U>("ArgU");
      var ArgV = new ParameterExpr<V>("ArgV");
      var ArgW = new ParameterExpr<W>("ArgW");
      var assign = new NowAssign<Action<S, U, V,W>>(ArgS, ArgU, ArgV, ArgW);
      var LHS0 = LHS(ArgS, ArgU, ArgV, ArgW);
      var RHS0 = LHS0.AsRHS(RHS(ArgS, ArgU, ArgV, ArgW));
      if (!LHS0.Provider.Solve(assign, RHS0)) throw new NotSupportedException();
      var list = assign.Results;
      // flatten the list!
      if (list.Count == 0) throw new NotSupportedException();
      else if (list.Count == 1) return list[0];
      else return (argA, argB, argC, argD) => {
        foreach (var n in list) n(argA, argB, argC, argD);
      };
    }


    private new class WithTServices : Brand<BRAND>.WithTServices {
      public BRAND CoerceFrom<T0, BRAND0>(BRAND0 value) where BRAND0 : Brand<T0, BRAND0> {
        return value.Coerce<T, BRAND>();
      }
      public BRAND DownCastFrom<T0, BRAND0>(BRAND0 value) where BRAND0 : Brand<T0, BRAND0>, IExtends<BRAND0,BRAND> {
        return ToBrand(Ops.DownCastOperator1<T0, T>.Instance.Make(value));
      }
    }
    public static void CanWrap<S>(Func<Expr<T>,Expr<S>> F, Func<Expr<S>,Expr<T>> G) {
      CanWrap<BRAND, S>.From = b => F(b.Provider);
      CanWrap<BRAND, S>.To = u => ToBrand(G(u));
    }

    static Brand() {
      //Converter<BRAND, ObjectBl>.Apply = brand => (ObjectBl)brand;

      CanWrap<T>(t => t, t => t);
      {
        var m = typeof(BRAND).GetConstructor(new Type[] { typeof(Expr<T>) });
        if (m == null) {
          var n = typeof(BRAND).GetMethod("Make", new Type[] { typeof(Expr<T>) });
          (n != null).Assert();
          ToBrand = e => (BRAND)n.Invoke(null, new object[] { e });
        } else {

          ToBrand = e => (BRAND)m.Invoke(new object[] { e });
        }
      }


      if (typeof(ICoreBrandT).IsAssignableFrom(typeof(BRAND))) RegisterT();
      if (default(T) != null) 
        Default = ToBrand(new Constant<T>(default(T)));
      CanWrap<BRAND, T>.To = (e) => ToBrand(e);
      CanWrap<BRAND, T>.From = (b) => b.Provider;

      BaseToBrand = (e) => ToBrand((Expr<T>)e);
      TypeOfT = typeof(T);

      TServices = new WithTServices();
      (RevealTypeF == null).Assert();
      RevealTypeF = (visitor) => visitor.Visit<T, BRAND>();
      Params<BRAND>.Make0 = (key, name) => ToBrand(new ParameterExpr<T>(key) { Name = name });

      Brand<BRAND>.UpCast0 = (brand) => UpCast0(brand);
      Brand<BRAND>.Assign = Assign;
    }


    public static implicit operator Expr<T>(Brand<T, BRAND> b) { return b.Provider; }
    internal BRAND0 DownCast<T0, BRAND0>()
      where T0 : T
      where BRAND0 : Brand<T0, BRAND0> {
      return Brand<T0,BRAND0>.ToBrand(DownCastOperator<T, T0>.Instance.Make(Provider));
    }
    /// <summary>
    /// Downcast this brand to specified extending brand.
    /// </summary>
    public BRAND0 DownCast<BRAND0>() where BRAND0 : Brand<BRAND0>, IExtends<BRAND0,BRAND> {
      return Brand<BRAND0>.CoerceFrom<T, BRAND>(this);
    }
    internal static BRAND UpCast<T0, BRAND0>(BRAND0 value)
      where T0 : T
      where BRAND0 : Brand<T0, BRAND0> {
      return ToBrand(UpCastOperator<T0, T>.Instance.Make(value.Provider)); 
    }
    /// <summary>
    /// Upcast value of extending brand to this brand.
    /// </summary>
    public static BRAND UpCast<BRAND0>(Brand<BRAND0> value) where BRAND0 : Brand<BRAND0>, IExtends<BRAND0,BRAND> {
      if (value is BRAND) return (BRAND)(object)value;
      else return value.Reveal(new UpCastVisitor());
    }
    public new static BRAND UpCast0(Brand value) {
      (value is IExtends<BRAND>).Assert();
      if (value is BRAND) return (BRAND)(object)value;
      else return value.Reveal(new UpCastVisitor());
    }
    protected class UpCastVisitor : BrandTypeReveal<BRAND> {
      public BRAND Visit<T0, BRAND0>(Brand<T0, BRAND0> value) where BRAND0 : Brand<T0, BRAND0> {
        return ToBrand(UpCastOperator1<T0, T>.Instance.Make(value.Provider));
      }
    }


    /// <summary>
    /// Coerce this value to a brand using a conversion routine (not an up or down cast).
    /// </summary>
    public BRAND0 Coerce<T0, BRAND0>()
      where BRAND0 : Brand<T0, BRAND0> {
      return Brand<T0,BRAND0>.ToBrand(ExplicitConvertOperator<T, T0>.Instance.Make(Provider));
    }
    /// <summary>
    /// Map this value to another of the same brand type.
    /// </summary>
    public BRAND MapM(Func<BRAND, BRAND> F) { return F(AsBrand); }

    /// <summary>
    /// Map this value to another value of a different type.
    /// </summary>
    /// <param name="F">Conversion function, pure</param>
    public BRAND0 Map<T0, BRAND0>(Func<T, T0> F)
      where BRAND0 : Brand<T0, BRAND0> {
      return Brand<T0,BRAND0>.ToBrand(new MapOperator<T, T0>(F).Make(this));
    }



    /// <summary>
    /// Map this value to another value of a different type. F is assumed to be time dependent.
    /// </summary>
    /// <param name="F">Conversion function, time dependent</param>
    public BRAND0 StatefulMap<T0, BRAND0>(Func<T, T0> F)
      where BRAND0 : ICanBind<BRAND0> {
      var e = new MapOperator<T, T0>(F) { CanOptimize = false }.Make(this);
      var to = CanWrap<BRAND0, T0>.To;
      if (to == null) {
        var x = typeof(BRAND0).GetConstructor(new Type[0]).Invoke(new object[0]);

      }

      return CanWrap<BRAND0, T0>.To(e);
    }
    public static BRAND State(Func<T> F) {
      var e = new MapOperator<T, T>(t => F()) { CanOptimize = false }.Make(new Constant<T>(default(T)));
      return ToBrand(e);
    }


    /// <summary>
    /// Map this value to another value of a different type. Result supports inversion.
    /// </summary>
    /// <param name="F">Conversion function</param>
    /// <param name="G">Inversion function</param>
    public BRAND0 Map<T0, BRAND0>(Func<T, T0> F, Func<T0, T> G)
      where BRAND0 : Brand<T0, BRAND0> {
      return Brand<T0, BRAND0>.ToBrand(new MapOperator<T, T0>(F) { G = G }.Make(this));
    }
    /// <summary>
    /// Map this value to another value of a different type. Result supports inversion.
    /// </summary>
    /// <param name="F">Conversion function</param>
    /// <param name="G">Inversion function</param>
    public BRAND Map(Func<T, T> F, Func<T, T> G) {
      return Map<T, BRAND>(F, G);
    }
    /// <summary>
    /// Map this value to another value of a different type.
    /// </summary>
    /// <param name="F">Conversion function</param>
    public BRAND Map(Func<T, T> F) {
      return Map<T, BRAND>(F, null);
    }
    /// <summary>
    /// Combine two values together in map.
    /// </summary>
    public CombineSet<T0, BRAND0> Combine<T0, BRAND0>(ICanBind<BRAND0> Other) where BRAND0 : ICanBind<BRAND0> {
      return new CombineSet<T0, BRAND0>() { Other = Other, Self = this };
    }
    public partial class CombineSet<T0, BRAND0> where BRAND0 : ICanBind<BRAND0> {
      internal ICanBind<BRAND0> Other;
      internal BRAND Self;
      /// <summary>
      /// General map that supports inversion on either argument.
      /// </summary>
      /// <param name="F">Conversion function</param>
      /// <param name="G">Inversion function for first parameter</param>
      /// <param name="H">Inversion function for second parameter</param>
      public BRAND1 Map<T1, BRAND1>(Func<T, T0, T1> F, Func<T1, T0, T> G, Func<T1, T, T0> H) where BRAND1 : ICanBind<BRAND1> {
        return CanWrap<BRAND1,T1>.To(new MapOperator<T, T0, T1>(F) {
          UTS = G, UST = H,
        }.Make(Self, CanWrap<BRAND0,T0>.From((BRAND0) Other)));
      }
      /// <summary>
      /// Map that supports inversion on the first argument.
      /// </summary>
      /// <param name="F">Conversion function</param>
      /// <param name="G">Inversion function for first parameter</param>
      public BRAND1 Map<T1, BRAND1>(Func<T, T0, T1> F, Func<T1, T0, T> G) where BRAND1 : Brand<T1, BRAND1> {
        return Map<T1, BRAND1>(F, G, null);
      }
      /// <summary>
      /// Map that supports inversion on the second argument.
      /// </summary>
      /// <param name="F">Conversion function</param>
      /// <param name="H">Inversion function for second parameter</param>
      public BRAND1 MapB<T1, BRAND1>(Func<T, T0, T1> F, Func<T1, T, T0> H) where BRAND1 : Brand<T1, BRAND1> {
        return Map<T1, BRAND1>(F, null, H);
      }
      /// <summary>
      /// Map that does not support inversion.
      /// </summary>
      /// <param name="F">Conversion function</param>
      public BRAND1 Map<T1, BRAND1>(Func<T, T0, T1> F) where BRAND1 : ICanBind<BRAND1> {
        return Map<T1, BRAND1>(F, null, null);
      }
      /// <summary>
      /// General map that supports inversion on either argument.
      /// </summary>
      /// <param name="F">Conversion function</param>
      /// <param name="G">Inversion function for first parameter</param>
      /// <param name="H">Inversion function for second parameter</param>
      public BRAND Map(Func<T, T0, T> F, Func<T, T0, T> G, Func<T, T, T0> H) {
        return Map<T, BRAND>(F, G, H);
      }
      /// <summary>
      /// Map that supports inversion on the first argument.
      /// </summary>
      /// <param name="F">Conversion function</param>
      /// <param name="G">Inversion function for first parameter</param>
      public BRAND Map(Func<T, T0, T> F, Func<T, T0, T> G) {
        return Map(F, G, null);
      }
      /// <summary>
      /// Map that supports inversion on the second argument.
      /// </summary>
      /// <param name="F">Conversion function</param>
      /// <param name="H">Inversion function for second parameter</param>
      public BRAND MapB(Func<T, T0, T> F, Func<T, T, T0> H) {
        return Map<T, BRAND>(F, null, H);
      }
      /// <summary>
      /// Map that does not support inversion.
      /// </summary>
      /// <param name="F">Conversion function</param>
      public BRAND Map(Func<T, T0, T> F) {
        return Map<T, BRAND>(F, null, null);
      }
    }
    public static implicit operator Brand<T, BRAND>(T t) {
      return ToBrand(new Constant<T>(t));
    }


    public override int GetHashCode() { return Provider.GetHashCode(); }
    public override bool Equals(object obj) {
      if (obj is Brand<T, BRAND>) return Provider.Equals(((Brand<T, BRAND>)obj).Provider);
      return base.Equals(obj);
    }
    /// <summary>
    /// General coercion.
    /// </summary>
    public override BRAND0 Coerce<BRAND0>() { return Brand<BRAND0>.CoerceFrom<T, BRAND>(this); }
  }



  public abstract partial class ObjectBl<T, BRAND> : Brand<T, BRAND> where BRAND : ObjectBl<T, BRAND> {
    public ObjectBl(Expr<T> Provider) : base(Provider) { }
    public ObjectBl() : base() { }
    static ObjectBl() {
    }
  }
  public partial class ObjectBl : ObjectBl<object, ObjectBl>, ICoreBrandT {
    public ObjectBl(Expr<object> Provider) : base(Provider) { }
    public ObjectBl() : base() { }
    public static implicit operator ObjectBl(Expr<object> Value) { return new ObjectBl(Value); }
    public static implicit operator ObjectBl(string Value) { return new Constant<object>(Value); }
    public override StringBl ToStringBl() { return Map<string, StringBl>(obj => obj.ToString()); }

  }



  //  string is unique
  public partial class StringBl : ObjectBl<string, StringBl>, ICoreBrandT {
    public StringBl(Expr<string> Provider) : base(Provider) { }
    public StringBl() : base() { }
    public static implicit operator StringBl(Expr<string> Value) { return new StringBl(Value); }
    public static implicit operator StringBl(string Value) { return new Constant<string>(Value); }
    static StringBl() { 
      CheckInit();
    }
    public override StringBl ToStringBl() { return this; }
    public static StringBl operator +(StringBl opA, StringBl opB) {
      return opA.Combine<string,StringBl>(opB).Map((s0, s1) => s0 + s1);
    }
    public StringBl ToLower() {
      throw new NotImplementedException();
    }
    public StringBl ToUpper() {
      throw new NotImplementedException();
    }
    public static void CheckInit() { Extensions.Trace("CheckInit: " + typeof(StringBl) + " ToBrand=" + ToBrand); }

  }
  public partial class EnumBl<T> : ValueBrand<T, EnumBl<T>>, ICoreBrandT {
    public EnumBl() : base() { }
    public EnumBl(Expr<T> v) : base(v) { }
    public static implicit operator EnumBl<T>(Expr<T> v) { return new EnumBl<T>(v); }
    public static implicit operator EnumBl<T>(T v) { return new Constant<T>(v); }
    public static explicit operator IntBl(EnumBl<T> v) {
      return Ops.ExplicitConvertOperator<T, int>.Instance.Make(v.Provider);
    }
    public static explicit operator EnumBl<T>(IntBl v) {
      return Ops.ExplicitConvertOperator<int,T>.Instance.Make(v.Provider);
    }
    public EnumBl<T> Clamp(EnumBl<T> Low, EnumBl<T> High) {
      return (EnumBl<T>) ((IntBl)this).Clamp((IntBl)Low, (IntBl)High);
    }
    public static IntBl operator-(EnumBl<T> op0, EnumBl<T> op1) {
      return ((IntBl)op0) - ((IntBl)op1);
    }

  }
  public static partial class BrandExtensions {
    public static StringBl Bl(this string Str) { return Str; }
  }

}
